package com.toher.project.jasypt;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;


/**
 * 数据库连接用户密码加密 参考com.toher.project.ApplicationTests测试用例说明
 */
@Aspect
@Component
@Slf4j
public class JasyptAspect {

    //注入加密类
    private final StringEncryptor stringEncryptor;

    // jasyptStringEncryptor 配置类中定义的名称
    public JasyptAspect(@Qualifier("jasyptStringEncryptor") StringEncryptor stringEncryptor) {
        this.stringEncryptor = stringEncryptor;
    }


    @Pointcut("@annotation(JasyptMethod)")
    public void pointCut() {
    }


    @SneakyThrows
    @Around("pointCut())")
    public Object jasyptAround(ProceedingJoinPoint joinPoint) {
        Object proceed;
        //获取注解类
        JasyptMethod jasyptMethod = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(JasyptMethod.class);
        //获取注解传递值
        String value = jasyptMethod.value();
        //获取参数
        Object[] args = joinPoint.getArgs();
        // 这里可以定义常量或枚举判断，博主就直接判断了
        if(value.equals("ENC")){
            for(int i=0 ; i < args.length ; i++){
                // 判断字符串还是对象
                if(args[i] instanceof String) {
                    args[i] = stringEncryptor.encrypt(String.valueOf(args[i]));
                }else {
                    //对象 还分集合还是单个对象
                    boolean isList = (args[i] instanceof List<?>);
                    handlerArgs(args[i], value, isList);
                }
            }
            proceed = joinPoint.proceed(args);
        }else{
            proceed = joinPoint.proceed();
            // 判断字符串还是对象
            if(proceed instanceof String) {
                proceed = stringEncryptor.decrypt(String.valueOf(proceed));
            }else {
                //对象 还分集合还是单个对象
                boolean isList = (proceed instanceof List<?>);
                handlerArgs(proceed, value, isList);
            }
        }
        return proceed;
    }

    /**
     * 处理对象加解密
     * @param obj 参数对象
     * @param value 加解密值
     * @param isList 是否集合
     */
    private void handlerArgs(Object obj , String value , boolean isList){
        if(isList){
            List<Object> objs = (List<Object>)obj;
            for(Object o : objs){
                handlerFields(o, value);
            }
        }else{
            handlerFields(obj, value);
        }
    }

    /**
     * 抽取公共处理字段加解密方法
     * @param obj
     * @param value
     */
    private void handlerFields(Object obj , String value){
        Field[] fields = obj.getClass().getDeclaredFields();
        for(Field field : fields){
            //判断是否存在注解
            boolean hasJasyptField = field.isAnnotationPresent(JasyptField.class);
            if (hasJasyptField) {
                try {
                    field.setAccessible(true);
                    String plaintextValue = null;
                    plaintextValue = (String)field.get(obj);
                    String handlerValue;
                    if(value.equals("ENC")){
                        handlerValue = stringEncryptor.encrypt(plaintextValue); //处理加密
                    }
                    else{
                        handlerValue = stringEncryptor.decrypt(plaintextValue); //处理解密
                    }
                    field.set(obj, handlerValue);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
